#include "drvNRF24L01.h"
#include "drvSPI.h"
#include "drvIOConfig.h"

///////////////////////////////////////////////////////////////////////////////
// Local functions
static void drvNRF24L01ShortDelay(void);
static void drv24L01InactivateCSN(void);


/*****************************************************************************/
/* High level driver functions                                               */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Initialize nRF24L01 chip
void comRFLinkInit(void)
{
	// init CE
	InitNRF24L01CE();
	NRF24L01CE(PIN_LOW);

	// init CSN
	InitNRF24L01CSN();
	NRF24L01CSN(PIN_HIGH);

	// init SPI
	drvSPIInit();

	// set RX mode initially and power up and enable RX interrupt
	drvNRF24L01WriteRegister(drvNRF24L01RA_CONFIG,
														drvNRF24L01CR_PRIM_RX | drvNRF24L01CR_EN_CRC | drvNRF24L01CR_CRCO | drvNRF24L01CR_MASK_MAX_RT | drvNRF24L01CR_MASK_TX_DS | drvNRF24L01CR_PWR_UP);

	// enable pipes
	drvNRF24L01WriteRegister(drvNRF24L01RA_EN_RXADDR, drvNRF24L01RXA_ERX_P0 | drvNRF24L01RXA_ERX_P1 );

	// setup address width (5bytes)
	drvNRF24L01WriteRegister(drvNRF24L01RA_SETUP_AW, drvNRF24L01SAW_5BYTES );

	// enable automatic retransmission
	drvNRF24L01WriteRegister(drvNRF24L01RA_EN_AA, drvNRF24L01EAA_ALL );

	// setup automatic retransmission
	drvNRF24L01WriteRegister(drvNRF24L01RA_SETUP_RETR, 0x13);

	// set default channel
	drvNRF24L01WriteRegister(drvNRF24L01RA_RF_CH, 2 );

	// RF setup
	drvNRF24L01WriteRegister(drvNRF24L01RA_RF_SETUP, drvNRF24L01SAR_LNA_HCURR | drvNRF24L01SAR_RF_PWR_0 );

	// Check if feature register is accessible
	drvNRF24L01WriteRegister( drvNRF24L01RA_DYNPD, drvNRF24L01DPL_ALL );
	if( drvNRF24L01ReadRegister( drvNRF24L01RA_DYNPD ) != drvNRF24L01DPL_ALL )
	{
		// Enable FEATURE register
		NRF24L01CSN(PIN_LOW);																		// CSN in low
 		drvSPISendAndReceiveByte(drvNRF24L01CMD_ACTIVATE);			// Send activate command
 		drvSPISendAndReceiveByte(drvNRF24L01CMD_ACTIVATE_DATA);	// Send activate data
		drv24L01InactivateCSN();                       					// CSN high, terminate SPI communication
	}

	// Now feature register is accessible
	// enable dynamic payload length and ack with payload
	drvNRF24L01WriteRegister( drvNRF24L01RA_FEATURE, drvNRF24L01F_EN_DPL | drvNRF24L01F_EN_ACK_PAY );

	// enable dynamic payload length on all pipes
	drvNRF24L01WriteRegister( drvNRF24L01RA_DYNPD, drvNRF24L01DPL_ALL );
}

/*****************************************************************************/
/* Address functions                                                         */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Sets transmit address
void drvNRF24L01SetTXAddress(dosByte* in_address, dosByte in_address_length )
{
	// check address length
	if( in_address_length <= 5 )
	{
		drvNRF24L01WriteRegisters( drvNRF24L01CMD_WRITE_REG + drvNRF24L01RA_TX_ADDR, in_address, in_address_length );
	}
}

///////////////////////////////////////////////////////////////////////////////
// Sets receive address
void drvNRF24L01SetRXAddress(dosByte in_pipe, dosByte* in_address, dosByte in_address_length )
{
	switch( in_pipe )
	{
		// pipe 0 and 1
		case 0:
		case 1:
			if( in_address_length <= 5 )
				drvNRF24L01WriteRegisters(drvNRF24L01CMD_WRITE_REG + drvNRF24L01RA_RX_ADDR_P0 + in_pipe, in_address, in_address_length );
			break;

		// pipe 2, 3, 4, 5
		case 2:
		case 3:
		case 4:
		case 5:
			if( in_address_length == 1 )
				drvNRF24L01WriteRegister(drvNRF24L01RA_RX_ADDR_P0 + in_pipe, *in_address );
			break;
			
	}
}


/*****************************************************************************/
/* Channel setup functions                                                   */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Sets RF channel
void drvNRF24L01SetChannel(dosByte in_channel)
{
  drvNRF24L01WriteRegister(drvNRF24L01RA_RF_CH, in_channel);
}

///////////////////////////////////////////////////////////////////////////////
// Gets current RF channel
dosByte drvNRF24L01GetChannel(void)
{
  return drvNRF24L01ReadRegister(drvNRF24L01RA_RF_CH);
}

/*****************************************************************************/
/* Transmitter functions                                                     */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// True if data has been sent
dosBool drvNRF24L01IsDataSent(void)
{
	dosByte status;

	status = drvNRF24L01ReadStatus();

	return (status & drvNRF24L01S_TX_DS) != 0;
}

///////////////////////////////////////////////////////////////////////////////
// True if there was no ack from the receiver
dosBool drvNRF24L01IsMaxRetry(void)
{
	dosByte status;

	status = drvNRF24L01ReadStatus();

	return (status & drvNRF24L01S_MAX_RT) != 0;
}

///////////////////////////////////////////////////////////////////////////////
// Send data
void drvNRF24L01WriteTXData( dosByte* in_buffer, dosByte in_length )
{
	dosByte config;

	// check parameters
	if( in_length >= drvNRF24L01_MAX_PAYLOAD_LENGTH )
		in_length = drvNRF24L01_MAX_PAYLOAD_LENGTH - 1;

	// clear interrupt status bits	
	drvNRF24L01WriteRegister(drvNRF24L01RA_STATUS, drvNRF24L01CR_MASK_TX_DS | drvNRF24L01CR_MASK_MAX_RT );

	// copy data to TX buffer
	drvNRF24L01WriteRegisters(drvNRF24L01CMD_WR_TX_PLOAD, in_buffer, in_length );

	// set TX mode
	config = drvNRF24L01ReadRegister(drvNRF24L01RA_CONFIG);
	config &= ~drvNRF24L01CR_PRIM_RX;
	drvNRF24L01WriteRegister(drvNRF24L01RA_CONFIG, config);

	// start transmission
	drvNRF24L01ShortDelay();
	NRF24L01CE(PIN_HIGH);
	drvNRF24L01ShortDelay();
	NRF24L01CE(PIN_LOW);
}

///////////////////////////////////////////////////////////////////////////////
// Flush TX buffer
dosByte drv24L01FlushTXBuffer(void)
{
	dosByte retval;

	NRF24L01CSN(PIN_LOW);														// CSN in low
  drvSPISendAndReceiveByte(drvNRF24L01CMD_FLUSH_TX);// Flush TX buffer
	drv24L01InactivateCSN();                       	// CSN high, terminate SPI communication

	// clear interrupt status bits	
	drvNRF24L01WriteRegister(drvNRF24L01RA_STATUS, drvNRF24L01CR_MASK_TX_DS );

	return retval;
}

///////////////////////////////////////////////////////////////////////////////
// Write ACK data
void drvNRF24L01WriteACKData( dosByte in_pipe, dosByte* in_buffer, dosByte in_length )
{
	// check parameters
	if( in_pipe >= drvNRF24L01_RX_PIPE_COUNT )
		in_pipe = drvNRF24L01_RX_PIPE_COUNT-1;

	if( in_length >= drvNRF24L01_MAX_PAYLOAD_LENGTH )
		in_length = drvNRF24L01_MAX_PAYLOAD_LENGTH - 1;

	// copy data to TX buffer
	drvNRF24L01WriteRegisters(drvNRF24L01CMD_W_ACK_PAYLOAD + in_pipe, in_buffer, in_length );
}

/*****************************************************************************/
/* Receiver functions                                                        */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Set RX mode
void drvNRF24L01SetRxMode(void)
{
	dosByte config;

	// set RX mode
	config = drvNRF24L01ReadRegister(drvNRF24L01RA_CONFIG);
	config |= drvNRF24L01CR_PRIM_RX;
	drvNRF24L01WriteRegister(drvNRF24L01RA_CONFIG, config);

	// enable chip
	NRF24L01CE(PIN_HIGH);
}

///////////////////////////////////////////////////////////////////////////////
// Is data received
dosBool drvNRF24L01IsDataReceived(void)
{
	return !NRF24L01INTIsSet();
}

///////////////////////////////////////////////////////////////////////////////
// Set RX data length
dosBool drvNRF24L01SetRXDataLength(dosByte in_pipe, dosByte in_length)
{
	dosByte status;

	if( in_pipe < drvNRF24L01_RX_PIPE_COUNT && in_length <= drvNRF24L01_MAX_PAYLOAD_LENGTH )
	{
		status = drvNRF24L01WriteRegister(drvNRF24L01RA_RX_PW_P0 + in_pipe, in_length );
	}

	return status;
}

///////////////////////////////////////////////////////////////////////////////
// Get RX pipe index
dosByte drvNRF24L01GetRXPipeNumber(void)
{
	// disable receiver
	NRF24L01CE(PIN_LOW);

	return (drvNRF24L01ReadStatus() & drvNRF24L01S_RX_P_NO) >> 1;
}

///////////////////////////////////////////////////////////////////////////////
// Get RX data length on the pecified pipe
dosByte drvNRF24L01GetRXDataLengthOnPipe(dosByte in_pipe)
{
	// disable receiver
	NRF24L01CE(PIN_LOW);

	return drvNRF24L01ReadRegister(drvNRF24L01RA_RX_PW_P0 + in_pipe);
}

///////////////////////////////////////////////////////////////////////////////
// Get RX data length
dosByte drvNRF24L01GetRXDataLength(void)
{
	// disable receiver
	NRF24L01CE(PIN_LOW);

	return drvNRF24L01ReadRegister( drvNRF24L01CMD_R_RX_PL_WID );
}

///////////////////////////////////////////////////////////////////////////////
// Get received message
void drvNRF24L01ReadRXData(dosByte* out_buffer, dosByte in_length )
{
	// disable receiver
	NRF24L01CE(PIN_LOW);

	// read data
	drvNRF24L01ReadRegisters( drvNRF24L01CMD_RD_RX_PLOAD, out_buffer, in_length );
	
	// clear interrupt
	drvNRF24L01WriteRegister(drvNRF24L01RA_STATUS, drvNRF24L01S_RX_DR );
}

/*****************************************************************************/
/* Low level functions                                                       */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Write byte into nRF24L01's register and return status register value
dosByte drvNRF24L01WriteRegister( dosByte in_address, dosByte in_data )
{
	dosByte status;

	NRF24L01CSN(PIN_LOW);														// CSN in low
	status = drvSPISendAndReceiveByte(drvNRF24L01CMD_WRITE_REG  + in_address);	// Select register to write to...
	drvSPISendAndReceiveByte(in_data);							// ..and write value to it..
	drv24L01InactivateCSN();                       	// CSN high, terminate SPI communication

	return(status);                                 // return nRF24L01 status byte
}

///////////////////////////////////////////////////////////////////////////////
// Read byte from nRF24L01's register
dosByte drvNRF24L01ReadRegister(dosByte in_address)
{
	dosByte retval;

	NRF24L01CSN(PIN_LOW);														// CSN in low
  drvSPISendAndReceiveByte(drvNRF24L01CMD_READ_REG + in_address);           // Select register to read from..
  retval = drvSPISendAndReceiveByte(0);						// ..then read register value
	drv24L01InactivateCSN();                       	// CSN high, terminate SPI communication

	return retval;
}

///////////////////////////////////////////////////////////////////////////////
// Reads status register
dosByte drvNRF24L01ReadStatus(void)
{
	dosByte retval;

	NRF24L01CSN(PIN_LOW);														// CSN in low
  retval = drvSPISendAndReceiveByte(drvNRF24L01CMD_NOP); // read status register
	drv24L01InactivateCSN();                       	// CSN high, terminate SPI communication

	return retval;
}

///////////////////////////////////////////////////////////////////////////////
// Read nRF24L01's registers into a buffer and return status
dosByte drvNRF24L01ReadRegisters( dosByte in_command, dosByte* in_buffer, dosByte in_buffer_length)
{
	dosByte status, byte_ctr;

	NRF24L01CSN(PIN_LOW);														// CSN in low
  status = drvSPISendAndReceiveByte(in_command);	// Select register to write to and read status byte

  for(byte_ctr=0; byte_ctr<in_buffer_length; byte_ctr++)
    in_buffer[byte_ctr] = drvSPISendAndReceiveByte(0); // Perform SPI_RW to read byte from nRF24L01

	drv24L01InactivateCSN();                       	// CSN high, terminate SPI communication

  return status;																	// return nRF24L01 status byte
}

///////////////////////////////////////////////////////////////////////////////
// Write buffer content into the registers of nRF24L01 and return status
dosByte drvNRF24L01WriteRegisters( dosByte in_command, dosByte* in_buffer, dosByte in_buffer_length)
{
	dosByte status, byte_ctr;

	NRF24L01CSN(PIN_LOW);														// CSN in low
  status = drvSPISendAndReceiveByte(in_command);	// Select register to write to and read status byte

  for(byte_ctr=0; byte_ctr<in_buffer_length; byte_ctr++) // then write all byte in buffer
    drvSPISendAndReceiveByte(*in_buffer++);

	drv24L01InactivateCSN();                       	// CSN high, terminate SPI communication

  return status;																	// return nRF24L01 status byte
}

///////////////////////////////////////////////////////////////////////////////
// Inactivate CSN
static void drv24L01InactivateCSN(void)
{
	dosByte i;
	dosByte j;
	
	// inactivate CSN
  NRF24L01CSN(PIN_HIGH);

	// short delay (min 50ns)
	j = 0;
	for(i = 0; i < NRF24L01_CSN_INACTIVE_LENGTH; i++ )
		j = i + 1;
}

///////////////////////////////////////////////////////////////////////////////
// Short delay for CE pulse
static void drvNRF24L01ShortDelay(void)
{
	dosByte i;
	dosByte j;
	
	j = 0;
	for(i = 0; i < NRF24L01_CE_PULSE_LENGTH; i++ )
		j = i + 1;
}
